#pragma once


#include <boost/unordered_map.hpp>

#include <engine.core/noncopyable.hpp>

#include "exports.hpp"
#include "Commands.hpp"
#include "ResponseTask.hpp"
#include "Queue.hpp"




namespace Engine
{
	namespace Debugger
	{
		/**
		 * A Controller is the counterpart to a Daemon.
		 * As the name suggests, it takes responsibility of controlling a Daemon.
		 *
		 * Using the controller is easy: Just like with a services::scheduler,
		 * you must call Controller::execute with an instance of a predefined
		 * command, for example AttachCommand. The function returns a services::future
		 * objec that can be queried for the state of the command (finished & return value).
		 *
		 * The Controller itself can be used out of the box, however both queues must be implemented
		 * properly, so that a controller and daemon instance can communicate properly:
		 *
		 * Controller::execute => CommandWriter => CommandReader => Daemon::receive
		 * Daemon::send => NotificationWriter => NotificationReader => Controller::receive
		 */
		class ENGINE_DEBUGGER_API Controller
			: public noncopyable
		{
		public:

			typedef boost::shared_ptr<Controller>   ptr;

			Controller(MessageWriter::ptr writer, MessageReader::ptr reader);
			virtual ~Controller();

			/**
			 * Sends the given command to the Daemon.
			 * It can be assumed that the command will most likely be processed
			 * asynchronously (maybe even in another process). In order to
			 * query the command for it's state, the function returns a future
			 * object that contains information about the command's progress & state.
			 *
			 * @param command   the command to send to the daemon
			 * @param timeout   the amount of time after which the command is timed out
			 */
			template <typename Command>
			services::future<typename Command::result_type> execute(const Command& command, boost::posix_time::time_duration timeout = boost::posix_time::seconds(10))
			{
				// The future object that keeps track of the command's status
				services::future<typename Command::result_type> future;

				// The task that does the actual work
				auto task = boost::make_shared<ResponseTask>(future, command, timeout);
				m_tasks.insert(std::make_pair(command.tag(), task));

				return future;
			}

			/**
			 * This function must be called regularly in order to pull responses
			 * from the queue.
			 */
			void pull();

		private:

			typedef boost::unordered_map<boost::uuids::uuid, ResponseTask::ptr>   Tasks;

			MessageWriter::ptr   m_writer;
			MessageReader::ptr   m_reader;
			Tasks                m_tasks;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
